/* src/interfaces/ecpg/pgtypeslib/interval.c */

#include "postgres_fe.h"

#include <time.h>
#include <math.h>
#include <limits.h>

#ifdef __FAST_MATH__
#error -ffast-math is known to break this code
#endif

#include "common/string.h"
#include "dt.h"
#include "pgtypes_error.h"
#include "pgtypes_interval.h"
#include "pgtypeslib_extern.h"

/* 从.../src/backend/utils/adt/datetime.c复制粘贴
 * 并将struct pg_tm更改为struct tm
 */
static void fc_AdjustFractSeconds(double fc_frac, struct /* pg_ */ tm *fc_tm, fsec_t *fc_fsec, int fc_scale)
{
	int			fc_sec;

	if (fc_frac == 0)
		return;
	fc_frac *= fc_scale;
	fc_sec = (int) fc_frac;
	fc_tm->tm_sec += fc_sec;
	fc_frac -= fc_sec;
	*fc_fsec += rint(fc_frac * 1000000);
}


/* 从.../src/backend/utils/adt/datetime.c复制粘贴
 * 并将struct pg_tm更改为struct tm
 */
static void fc_AdjustFractDays(double fc_frac, struct /* pg_ */ tm *fc_tm, fsec_t *fc_fsec, int fc_scale)
{
	int			fc_extra_days;

	if (fc_frac == 0)
		return;
	fc_frac *= fc_scale;
	fc_extra_days = (int) fc_frac;
	fc_tm->tm_mday += fc_extra_days;
	fc_frac -= fc_extra_days;
	fc_AdjustFractSeconds(fc_frac, fc_tm, fc_fsec, SECS_PER_DAY);
}

/* 从.../src/backend/utils/adt/datetime.c复制粘贴 */
static int fc_ParseISO8601Number(const char *fc_str, char **fc_endptr, int *fc_ipart, double *fc_fpart)
{
	double		fc_val;

	if (!(isdigit((unsigned char) *fc_str) || *fc_str == '-' || *fc_str == '.'))
		return DTERR_BAD_FORMAT;
	errno = 0;
	fc_val = strtod(fc_str, fc_endptr);
	/* 我们没有看到任何看起来像双精度数的东西吗？ */
	if (*fc_endptr == fc_str || errno != 0)
		return DTERR_BAD_FORMAT;
	/* 注意溢出 */
	if (fc_val < INT_MIN || fc_val > INT_MAX)
		return DTERR_FIELD_OVERFLOW;
	/* 确保我们朝零截断（参见dtrunc()） */
	if (fc_val >= 0)
		*fc_ipart = (int) floor(fc_val);
	else
		*fc_ipart = (int) -floor(-fc_val);
	*fc_fpart = fc_val - *fc_ipart;
	return 0;
}

/* 从.../src/backend/utils/adt/datetime.c复制粘贴 */
static int fc_ISO8601IntegerWidth(const char *fc_fieldstart)
{
	/* 我们可能有一个前导'-' */
	if (*fc_fieldstart == '-')
		fc_fieldstart++;
	return strspn(fc_fieldstart, "0123456789");
}


/* 从.../src/backend/utils/adt/datetime.c复制粘贴
 * 并将struct pg_tm更改为struct tm
 */
static inline void fc_ClearPgTm(struct /* pg_ */ tm *fc_tm, fsec_t *fc_fsec)
{
	fc_tm->tm_year = 0;
	fc_tm->tm_mon = 0;
	fc_tm->tm_mday = 0;
	fc_tm->tm_hour = 0;
	fc_tm->tm_min = 0;
	fc_tm->tm_sec = 0;
	*fc_fsec = 0;
}

/* 从.../src/backend/utils/adt/datetime.c复制粘贴
 *
 * * 将struct pg_tm更改为struct tm
 *
 * * 将函数设为静态
 */
static int fc_DecodeISO8601Interval(char *fc_str,
					  int *fc_dtype, struct /* pg_ */ tm *fc_tm, fsec_t *fc_fsec)
{
	bool		fc_datepart = true;
	bool		fc_havefield = false;

	*fc_dtype = DTK_DELTA;
	fc_ClearPgTm(fc_tm, fc_fsec);

	if (strlen(fc_str) < 2 || fc_str[0] != 'P')
		return DTERR_BAD_FORMAT;

	fc_str++;
	while (*fc_str)
	{
		char	   *fc_fieldstart;
		int			fc_val;
		double		fc_fval;
		char		fc_unit;
		int			fc_dterr;

		if (*fc_str == 'T')		/* T表示时间部分的开始 */
		{
			fc_datepart = false;
			fc_havefield = false;
			fc_str++;
			continue;
		}

		fc_fieldstart = fc_str;
		fc_dterr = fc_ParseISO8601Number(fc_str, &fc_str, &fc_val, &fc_fval);
		if (fc_dterr)
			return fc_dterr;

		/*
		 * 注意：我们可能会在这里超出字符串的末尾。下面的代码
		 * *必须*在unit == '\0'时退出循环。
		 */
		fc_unit = *fc_str++;

		if (fc_datepart)
		{
			switch (fc_unit)		/* T之前：年 月 周 日 */
			{
				case 'Y':
					fc_tm->tm_year += fc_val;
					fc_tm->tm_mon += rint(fc_fval * MONTHS_PER_YEAR);
					break;
				case 'M':
					fc_tm->tm_mon += fc_val;
					fc_AdjustFractDays(fc_fval, fc_tm, fc_fsec, DAYS_PER_MONTH);
					break;
				case 'W':
					fc_tm->tm_mday += fc_val * 7;
					fc_AdjustFractDays(fc_fval, fc_tm, fc_fsec, 7);
					break;
				case 'D':
					fc_tm->tm_mday += fc_val;
					fc_AdjustFractSeconds(fc_fval, fc_tm, fc_fsec, SECS_PER_DAY);
					break;
				case 'T':		/* ISO 8601 4.4.3.3 替代格式 / 基本 */
				case '\0':
					if (fc_ISO8601IntegerWidth(fc_fieldstart) == 8 && !fc_havefield)
					{
						fc_tm->tm_year += fc_val / 10000;
						fc_tm->tm_mon += (fc_val / 100) % 100;
						fc_tm->tm_mday += fc_val % 100;
						fc_AdjustFractSeconds(fc_fval, fc_tm, fc_fsec, SECS_PER_DAY);
						if (fc_unit == '\0')
							return 0;
						fc_datepart = false;
						fc_havefield = false;
						continue;
					}
					/* FALLTHROUGH */
					/* 否则跳转到扩展替代格式 */
					/************************************************************
	 * 通过其键获取准备好的计划描述符
	 ************************************************************/
				case '-':		/* ISO 8601 4.4.3.3 替代格式，
								 * 扩展 */
					if (fc_havefield)
						return DTERR_BAD_FORMAT;

					fc_tm->tm_year += fc_val;
					fc_tm->tm_mon += rint(fc_fval * MONTHS_PER_YEAR);
					if (fc_unit == '\0')
						return 0;
					if (fc_unit == 'T')
					{
						fc_datepart = false;
						fc_havefield = false;
						continue;
					}

					fc_dterr = fc_ParseISO8601Number(fc_str, &fc_str, &fc_val, &fc_fval);
					if (fc_dterr)
						return fc_dterr;
					fc_tm->tm_mon += fc_val;
					fc_AdjustFractDays(fc_fval, fc_tm, fc_fsec, DAYS_PER_MONTH);
					if (*fc_str == '\0')
						return 0;
					if (*fc_str == 'T')
					{
						fc_datepart = false;
						fc_havefield = false;
						continue;
					}
					if (*fc_str != '-')
						return DTERR_BAD_FORMAT;
					fc_str++;

					fc_dterr = fc_ParseISO8601Number(fc_str, &fc_str, &fc_val, &fc_fval);
					if (fc_dterr)
						return fc_dterr;
					fc_tm->tm_mday += fc_val;
					fc_AdjustFractSeconds(fc_fval, fc_tm, fc_fsec, SECS_PER_DAY);
					if (*fc_str == '\0')
						return 0;
					if (*fc_str == 'T')
					{
						fc_datepart = false;
						fc_havefield = false;
						continue;
					}
					return DTERR_BAD_FORMAT;
				default:
					/* 不是有效的日期单位后缀 */
					return DTERR_BAD_FORMAT;
			}
		}
		else
		{
			switch (fc_unit)		/* T之后：小时 分 秒 */
			{
				case 'H':
					fc_tm->tm_hour += fc_val;
					fc_AdjustFractSeconds(fc_fval, fc_tm, fc_fsec, SECS_PER_HOUR);
					break;
				case 'M':
					fc_tm->tm_min += fc_val;
					fc_AdjustFractSeconds(fc_fval, fc_tm, fc_fsec, SECS_PER_MINUTE);
					break;
				case 'S':
					fc_tm->tm_sec += fc_val;
					fc_AdjustFractSeconds(fc_fval, fc_tm, fc_fsec, 1);
					break;
				case '\0':		/* ISO 8601 4.4.3.3 替代格式 */
					if (fc_ISO8601IntegerWidth(fc_fieldstart) == 6 && !fc_havefield)
					{
						fc_tm->tm_hour += fc_val / 10000;
						fc_tm->tm_min += (fc_val / 100) % 100;
						fc_tm->tm_sec += fc_val % 100;
						fc_AdjustFractSeconds(fc_fval, fc_tm, fc_fsec, 1);
						return 0;
					}
					/* FALLTHROUGH */
					/* 否则跳转到扩展替代格式 */
					/************************************************************
	 * 通过其键获取准备好的计划描述符
	 ************************************************************/
				case ':':		/* ISO 8601 4.4.3.3 替代格式，
								 * 扩展 */
					if (fc_havefield)
						return DTERR_BAD_FORMAT;

					fc_tm->tm_hour += fc_val;
					fc_AdjustFractSeconds(fc_fval, fc_tm, fc_fsec, SECS_PER_HOUR);
					if (fc_unit == '\0')
						return 0;

					fc_dterr = fc_ParseISO8601Number(fc_str, &fc_str, &fc_val, &fc_fval);
					if (fc_dterr)
						return fc_dterr;
					fc_tm->tm_min += fc_val;
					fc_AdjustFractSeconds(fc_fval, fc_tm, fc_fsec, SECS_PER_MINUTE);
					if (*fc_str == '\0')
						return 0;
					if (*fc_str != ':')
						return DTERR_BAD_FORMAT;
					fc_str++;

					fc_dterr = fc_ParseISO8601Number(fc_str, &fc_str, &fc_val, &fc_fval);
					if (fc_dterr)
						return fc_dterr;
					fc_tm->tm_sec += fc_val;
					fc_AdjustFractSeconds(fc_fval, fc_tm, fc_fsec, 1);
					if (*fc_str == '\0')
						return 0;
					return DTERR_BAD_FORMAT;

				default:
					/* 不是有效的时间单位后缀 */
					return DTERR_BAD_FORMAT;
			}
		}

		fc_havefield = true;
	}

	return 0;
}



/* 从.../src/backend/utils/adt/datetime.c复制粘贴
 * 例外有3个
 *
 *	* 将struct pg_tm更改为struct tm
 *
 *	* ECPG代码在没有'range'参数的情况下调用此函数
 *	  从参数列表和
 *	  调用DecodeTime的地方移除了'int range'；并添加
 *		 int range = INTERVAL_FULL_RANGE;
 *
 *	* ECPG似乎没有全局的IntervalStyle
 *	  因此添加
 *		int IntervalStyle = INTSTYLE_POSTGRES;
 */
int DecodeInterval(char **fc_field, int *fc_ftype, int fc_nf,	/* int range, */
			   int *fc_dtype, struct /* pg_ */ tm *fc_tm, fsec_t *fc_fsec)
{
	int			fc_IntervalStyle = INTSTYLE_POSTGRES_VERBOSE;
	int			fc_range = INTERVAL_FULL_RANGE;
	bool		fc_is_before = false;
	char	   *fc_cp;
	int			fc_fmask = 0,
				fc_tmask,
				fc_type;
	int			fc_i;
	int			fc_dterr;
	int			fc_val;
	double		fc_fval;

	*fc_dtype = DTK_DELTA;
	fc_type = IGNORE_DTF;
	fc_ClearPgTm(fc_tm, fc_fsec);

	/* 逆向遍历列表以获取值之前的单位 */
	for (fc_i = fc_nf - 1; fc_i >= 0; fc_i--)
	{
		switch (fc_ftype[fc_i])
		{
			case DTK_TIME:
				fc_dterr = DecodeTime(fc_field[fc_i],	/* range, */
								   &fc_tmask, fc_tm, fc_fsec);
				if (fc_dterr)
					return fc_dterr;
				fc_type = DTK_DAY;
				break;

			case DTK_TZ:

				/*
				 * 时区是具有前导符号字符和至少一个数字的标记；
				 * 其中可能包含':'、'.'、'-'。
				 */
				Assert(*fc_field[fc_i] == '-' || *fc_field[fc_i] == '+');

				/*
				 * 尝试hh:mm或hh:mm:ss。如果不行，则跳转到
				 * DTK_NUMBER案例，该案例可以处理带符号的浮点数和
				 * 带符号的年-月值。
				 */
				if (strchr(fc_field[fc_i] + 1, ':') != NULL &&
					DecodeTime(fc_field[fc_i] + 1,	/* INTERVAL_FULL_RANGE, */
							   &fc_tmask, fc_tm, fc_fsec) == 0)
				{
					if (*fc_field[fc_i] == '-')
					{
						/* 翻转所有字段的符号 */
						fc_tm->tm_hour = -fc_tm->tm_hour;
						fc_tm->tm_min = -fc_tm->tm_min;
						fc_tm->tm_sec = -fc_tm->tm_sec;
						*fc_fsec = -(*fc_fsec);
					}

					/*
					 * 如果未指定单位，则将下一个类型设为天。
					 * 这处理'1 +02:03'的情况，因为我们
					 * 从右到左读取。
					 */
					fc_type = DTK_DAY;
					fc_tmask = DTK_M(TZ);
					break;
				}
				/* FALL THROUGH */

			case DTK_DATE:
			case DTK_NUMBER:
				if (fc_type == IGNORE_DTF)
				{
					/* 使用typmod来决定最右边的字段是什么 */
					switch (fc_range)
					{
						case INTERVAL_MASK(YEAR):
							fc_type = DTK_YEAR;
							break;
						case INTERVAL_MASK(MONTH):
						case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
							fc_type = DTK_MONTH;
							break;
						case INTERVAL_MASK(DAY):
							fc_type = DTK_DAY;
							break;
						case INTERVAL_MASK(HOUR):
						case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
						case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
						case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
							fc_type = DTK_HOUR;
							break;
						case INTERVAL_MASK(MINUTE):
						case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
							fc_type = DTK_MINUTE;
							break;
						case INTERVAL_MASK(SECOND):
						case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
						case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
							fc_type = DTK_SECOND;
							break;
						default:
							fc_type = DTK_SECOND;
							break;
					}
				}

				errno = 0;
				fc_val = strtoint(fc_field[fc_i], &fc_cp, 10);
				if (errno == ERANGE)
					return DTERR_FIELD_OVERFLOW;

				if (*fc_cp == '-')
				{
					/* SQL "年-月"语法 */
					int			fc_val2;

					fc_val2 = strtoint(fc_cp + 1, &fc_cp, 10);
					if (errno == ERANGE || fc_val2 < 0 || fc_val2 >= MONTHS_PER_YEAR)
						return DTERR_FIELD_OVERFLOW;
					if (*fc_cp != '\0')
						return DTERR_BAD_FORMAT;
					fc_type = DTK_MONTH;
					if (*fc_field[fc_i] == '-')
						fc_val2 = -fc_val2;
					fc_val = fc_val * MONTHS_PER_YEAR + fc_val2;
					fc_fval = 0;
				}
				else if (*fc_cp == '.')
				{
					errno = 0;
					fc_fval = strtod(fc_cp, &fc_cp);
					if (*fc_cp != '\0' || errno != 0)
						return DTERR_BAD_FORMAT;

					if (*fc_field[fc_i] == '-')
						fc_fval = -fc_fval;
				}
				else if (*fc_cp == '\0')
					fc_fval = 0;
				else
					return DTERR_BAD_FORMAT;

				fc_tmask = 0;		/* DTK_M(type); */

				switch (fc_type)
				{
					case DTK_MICROSEC:
						*fc_fsec += rint(fc_val + fc_fval);
						fc_tmask = DTK_M(MICROSECOND);
						break;

					case DTK_MILLISEC:
						*fc_fsec += rint((fc_val + fc_fval) * 1000);
						fc_tmask = DTK_M(MILLISECOND);
						break;

					case DTK_SECOND:
						fc_tm->tm_sec += fc_val;
						*fc_fsec += rint(fc_fval * 1000000);

						/*
						 * 如果指定了任何小数秒，则将微秒和毫秒输入也考虑在内。
						 */
						if (fc_fval == 0)
							fc_tmask = DTK_M(SECOND);
						else
							fc_tmask = DTK_ALL_SECS_M;
						break;

					case DTK_MINUTE:
						fc_tm->tm_min += fc_val;
						fc_AdjustFractSeconds(fc_fval, fc_tm, fc_fsec, SECS_PER_MINUTE);
						fc_tmask = DTK_M(MINUTE);
						break;

					case DTK_HOUR:
						fc_tm->tm_hour += fc_val;
						fc_AdjustFractSeconds(fc_fval, fc_tm, fc_fsec, SECS_PER_HOUR);
						fc_tmask = DTK_M(HOUR);
						fc_type = DTK_DAY;
						break;

					case DTK_DAY:
						fc_tm->tm_mday += fc_val;
						fc_AdjustFractSeconds(fc_fval, fc_tm, fc_fsec, SECS_PER_DAY);
						fc_tmask = (fc_fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
						break;

					case DTK_WEEK:
						fc_tm->tm_mday += fc_val * 7;
						fc_AdjustFractDays(fc_fval, fc_tm, fc_fsec, 7);
						fc_tmask = (fc_fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
						break;

					case DTK_MONTH:
						fc_tm->tm_mon += fc_val;
						fc_AdjustFractDays(fc_fval, fc_tm, fc_fsec, DAYS_PER_MONTH);
						fc_tmask = DTK_M(MONTH);
						break;

					case DTK_YEAR:
						fc_tm->tm_year += fc_val;
						fc_tm->tm_mon += rint(fc_fval * MONTHS_PER_YEAR);
						fc_tmask = (fc_fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
						break;

					case DTK_DECADE:
						fc_tm->tm_year += fc_val * 10;
						fc_tm->tm_mon += rint(fc_fval * MONTHS_PER_YEAR * 10);
						fc_tmask = (fc_fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
						break;

					case DTK_CENTURY:
						fc_tm->tm_year += fc_val * 100;
						fc_tm->tm_mon += rint(fc_fval * MONTHS_PER_YEAR * 100);
						fc_tmask = (fc_fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
						break;

					case DTK_MILLENNIUM:
						fc_tm->tm_year += fc_val * 1000;
						fc_tm->tm_mon += rint(fc_fval * MONTHS_PER_YEAR * 1000);
						fc_tmask = (fc_fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
						break;

					default:
						return DTERR_BAD_FORMAT;
				}
				break;

			case DTK_STRING:
			case DTK_SPECIAL:
				fc_type = DecodeUnits(fc_i, fc_field[fc_i], &fc_val);
				if (fc_type == IGNORE_DTF)
					continue;

				fc_tmask = 0;		/* DTK_M(type); */
				switch (fc_type)
				{
					case UNITS:
						fc_type = fc_val;
						break;

					case AGO:
						fc_is_before = true;
						fc_type = fc_val;
						break;

					case RESERV:
						fc_tmask = (DTK_DATE_M | DTK_TIME_M);
						*fc_dtype = fc_val;
						break;

					default:
						return DTERR_BAD_FORMAT;
				}
				break;

			default:
				return DTERR_BAD_FORMAT;
		}

		if (fc_tmask & fc_fmask)
			return DTERR_BAD_FORMAT;
		fc_fmask |= fc_tmask;
	}

	/* 确保至少找到一个时间字段 */
	if (fc_fmask == 0)
		return DTERR_BAD_FORMAT;

	/* 确保分数秒是分数的 */
	if (*fc_fsec != 0)
	{
		int			fc_sec;

		fc_sec = *fc_fsec / USECS_PER_SEC;
		*fc_fsec -= fc_sec * USECS_PER_SEC;
		fc_tm->tm_sec += fc_sec;
	}

	/*----------
	 * SQL标准定义了区间文字
	 *	 '-1 1:00:00'
	 * 意味着“负1天和负1小时”，而Postgres
	 * 传统上将其视为“负1天和正1小时”。在SQL_STANDARD intervalstyle中，如果没有其他显式符号
	 * ，我们将前导符号应用于所有字段。
	 *
	 * 如果有其他显式符号，我们则保持符号不变。
	 * 这保护我们不误解postgres风格的转储输出，
	 * 因为postgres风格输出代码总是在负字段之后的所有字段上放置一个显式符号。
	 * 但请注意，SQL规范输出是模糊的，可能在加载时被误解！(因此最好实践
	 * 以postgres风格转储，而不是SQL风格。)
	 *----------
	 */
	if (fc_IntervalStyle == INTSTYLE_SQL_STANDARD && *fc_field[0] == '-')
	{
		/* 检查其他显式符号 */
		bool		fc_more_signs = false;

		for (fc_i = 1; fc_i < fc_nf; fc_i++)
		{
			if (*fc_field[fc_i] == '-' || *fc_field[fc_i] == '+')
			{
				fc_more_signs = true;
				break;
			}
		}

		if (!fc_more_signs)
		{
			/*
			 * 与其重新确定字段[0]是哪一个，不如强制
			 * 所有字段为负。
			 */
			if (*fc_fsec > 0)
				*fc_fsec = -(*fc_fsec);
			if (fc_tm->tm_sec > 0)
				fc_tm->tm_sec = -fc_tm->tm_sec;
			if (fc_tm->tm_min > 0)
				fc_tm->tm_min = -fc_tm->tm_min;
			if (fc_tm->tm_hour > 0)
				fc_tm->tm_hour = -fc_tm->tm_hour;
			if (fc_tm->tm_mday > 0)
				fc_tm->tm_mday = -fc_tm->tm_mday;
			if (fc_tm->tm_mon > 0)
				fc_tm->tm_mon = -fc_tm->tm_mon;
			if (fc_tm->tm_year > 0)
				fc_tm->tm_year = -fc_tm->tm_year;
		}
	}

	/* 最后，AGO使一切为负 */
	if (fc_is_before)
	{
		*fc_fsec = -(*fc_fsec);
		fc_tm->tm_sec = -fc_tm->tm_sec;
		fc_tm->tm_min = -fc_tm->tm_min;
		fc_tm->tm_hour = -fc_tm->tm_hour;
		fc_tm->tm_mday = -fc_tm->tm_mday;
		fc_tm->tm_mon = -fc_tm->tm_mon;
		fc_tm->tm_year = -fc_tm->tm_year;
	}

	return 0;
}


/* 从.../src/backend/utils/adt/datetime.c复制粘贴 */
static char * fc_AddVerboseIntPart(char *fc_cp, int fc_value, const char *fc_units,
				  bool *fc_is_zero, bool *fc_is_before)
{
	if (fc_value == 0)
		return fc_cp;
	/* 第一个非零值设置is_before */
	if (*fc_is_zero)
	{
		*fc_is_before = (fc_value < 0);
		fc_value = abs(fc_value);
	}
	else if (*fc_is_before)
		fc_value = -fc_value;
	sprintf(fc_cp, " %d %s%s", fc_value, fc_units, (fc_value == 1) ? "" : "s");
	*fc_is_zero = false;
	return fc_cp + strlen(fc_cp);
}

/* 从.../src/backend/utils/adt/datetime.c复制粘贴 */
static char * fc_AddPostgresIntPart(char *fc_cp, int fc_value, const char *fc_units,
				   bool *fc_is_zero, bool *fc_is_before)
{
	if (fc_value == 0)
		return fc_cp;
	sprintf(fc_cp, "%s%s%d %s%s",
			(!*fc_is_zero) ? " " : "",
			(*fc_is_before && fc_value > 0) ? "+" : "",
			fc_value,
			fc_units,
			(fc_value != 1) ? "s" : "");

	/*
	 * 每个非零字段仅为下一个字段设置is_before。这有点奇怪，但
	 * 这就是之前的工作方式...
	 */
	*fc_is_before = (fc_value < 0);
	*fc_is_zero = false;
	return fc_cp + strlen(fc_cp);
}

/* 从.../src/backend/utils/adt/datetime.c复制粘贴 */
static char * fc_AddISO8601IntPart(char *fc_cp, int fc_value, char fc_units)
{
	if (fc_value == 0)
		return fc_cp;
	sprintf(fc_cp, "%d%c", fc_value, fc_units);
	return fc_cp + strlen(fc_cp);
}

/* 从.../src/backend/utils/adt/datetime.c复制粘贴 */
static void fc_AppendSeconds(char *fc_cp, int fc_sec, fsec_t fc_fsec, int fc_precision, bool fc_fillzeros)
{
	if (fc_fsec == 0)
	{
		if (fc_fillzeros)
			sprintf(fc_cp, "%02d", abs(fc_sec));
		else
			sprintf(fc_cp, "%d", abs(fc_sec));
	}
	else
	{
		if (fc_fillzeros)
			sprintf(fc_cp, "%02d.%0*d", abs(fc_sec), fc_precision, (int) Abs(fc_fsec));
		else
			sprintf(fc_cp, "%d.%0*d", abs(fc_sec), fc_precision, (int) Abs(fc_fsec));
		TrimTrailingZeros(fc_cp);
	}
}


/* 从 .../src/backend/utils/adt/datetime.c 复制&粘贴
 *
 * 将pg_tm更改为tm
 */

void EncodeInterval(struct /* pg_ */ tm *fc_tm, fsec_t fc_fsec, int fc_style, char *fc_str)
{
	char	   *fc_cp = fc_str;
	int			fc_year = fc_tm->tm_year;
	int			fc_mon = fc_tm->tm_mon;
	int			fc_mday = fc_tm->tm_mday;
	int			fc_hour = fc_tm->tm_hour;
	int			fc_min = fc_tm->tm_min;
	int			fc_sec = fc_tm->tm_sec;
	bool		fc_is_before = false;
	bool		fc_is_zero = true;

	/*
	 * 年和月的符号保证匹配，因为它们
	 * 在内部存储为“月”。但在确定天和小时/分钟/秒
	 * 字段的符号时，我们需要检查is_before和is_zero。
	 */
	switch (fc_style)
	{
			/* SQL标准区间格式 */
		case INTSTYLE_SQL_STANDARD:
			{
				bool		fc_has_negative = fc_year < 0 || fc_mon < 0 ||
				fc_mday < 0 || fc_hour < 0 ||
				fc_min < 0 || fc_sec < 0 || fc_fsec < 0;
				bool		fc_has_positive = fc_year > 0 || fc_mon > 0 ||
				fc_mday > 0 || fc_hour > 0 ||
				fc_min > 0 || fc_sec > 0 || fc_fsec > 0;
				bool		fc_has_year_month = fc_year != 0 || fc_mon != 0;
				bool		fc_has_day_time = fc_mday != 0 || fc_hour != 0 ||
				fc_min != 0 || fc_sec != 0 || fc_fsec != 0;
				bool		fc_has_day = fc_mday != 0;
				bool		fc_sql_standard_value = !(fc_has_negative && fc_has_positive) &&
				!(fc_has_year_month && fc_has_day_time);

				/*
				 * SQL标准只希望在整个
				 * 区间前面有1个“<符号>”...但如果符号混合就做不到。
				 */
				if (fc_has_negative && fc_sql_standard_value)
				{
					*fc_cp++ = '-';
					fc_year = -fc_year;
					fc_mon = -fc_mon;
					fc_mday = -fc_mday;
					fc_hour = -fc_hour;
					fc_min = -fc_min;
					fc_sec = -fc_sec;
					fc_fsec = -fc_fsec;
				}

				if (!fc_has_negative && !fc_has_positive)
				{
					sprintf(fc_cp, "0");
				}
				else if (!fc_sql_standard_value)
				{
					/*
					 * 对于非SQL标准区间值，强制输出
					 * 符号以避免与
					 * 混合符号成分的区间模糊不清。
					 */
					char		fc_year_sign = (fc_year < 0 || fc_mon < 0) ? '-' : '+';
					char		fc_day_sign = (fc_mday < 0) ? '-' : '+';
					char		fc_sec_sign = (fc_hour < 0 || fc_min < 0 ||
											fc_sec < 0 || fc_fsec < 0) ? '-' : '+';

					sprintf(fc_cp, "%c%d-%d %c%d %c%d:%02d:",
							fc_year_sign, abs(fc_year), abs(fc_mon),
							fc_day_sign, abs(fc_mday),
							fc_sec_sign, abs(fc_hour), abs(fc_min));
					fc_cp += strlen(fc_cp);
					fc_AppendSeconds(fc_cp, fc_sec, fc_fsec, MAX_INTERVAL_PRECISION, true);
				}
				else if (fc_has_year_month)
				{
					sprintf(fc_cp, "%d-%d", fc_year, fc_mon);
				}
				else if (fc_has_day)
				{
					sprintf(fc_cp, "%d %d:%02d:", fc_mday, fc_hour, fc_min);
					fc_cp += strlen(fc_cp);
					fc_AppendSeconds(fc_cp, fc_sec, fc_fsec, MAX_INTERVAL_PRECISION, true);
				}
				else
				{
					sprintf(fc_cp, "%d:%02d:", fc_hour, fc_min);
					fc_cp += strlen(fc_cp);
					fc_AppendSeconds(fc_cp, fc_sec, fc_fsec, MAX_INTERVAL_PRECISION, true);
				}
			}
			break;

			/* ISO 8601 “仅依靠持续时间的时间间隔” */
		case INTSTYLE_ISO_8601:
			/* 特殊情况零以避免打印为空 */
			if (fc_year == 0 && fc_mon == 0 && fc_mday == 0 &&
				fc_hour == 0 && fc_min == 0 && fc_sec == 0 && fc_fsec == 0)
			{
				sprintf(fc_cp, "PT0S");
				break;
			}
			*fc_cp++ = 'P';
			fc_cp = fc_AddISO8601IntPart(fc_cp, fc_year, 'Y');
			fc_cp = fc_AddISO8601IntPart(fc_cp, fc_mon, 'M');
			fc_cp = fc_AddISO8601IntPart(fc_cp, fc_mday, 'D');
			if (fc_hour != 0 || fc_min != 0 || fc_sec != 0 || fc_fsec != 0)
				*fc_cp++ = 'T';
			fc_cp = fc_AddISO8601IntPart(fc_cp, fc_hour, 'H');
			fc_cp = fc_AddISO8601IntPart(fc_cp, fc_min, 'M');
			if (fc_sec != 0 || fc_fsec != 0)
			{
				if (fc_sec < 0 || fc_fsec < 0)
					*fc_cp++ = '-';
				fc_AppendSeconds(fc_cp, fc_sec, fc_fsec, MAX_INTERVAL_PRECISION, false);
				fc_cp += strlen(fc_cp);
				*fc_cp++ = 'S';
				*fc_cp = '\0';
			}
			break;

			/* 与postgresql < 8.4兼容，当DateStyle = 'iso'时 */
		case INTSTYLE_POSTGRES:
			fc_cp = fc_AddPostgresIntPart(fc_cp, fc_year, "year", &fc_is_zero, &fc_is_before);
			fc_cp = fc_AddPostgresIntPart(fc_cp, fc_mon, "mon", &fc_is_zero, &fc_is_before);
			fc_cp = fc_AddPostgresIntPart(fc_cp, fc_mday, "day", &fc_is_zero, &fc_is_before);
			if (fc_is_zero || fc_hour != 0 || fc_min != 0 || fc_sec != 0 || fc_fsec != 0)
			{
				bool		fc_minus = (fc_hour < 0 || fc_min < 0 || fc_sec < 0 || fc_fsec < 0);

				sprintf(fc_cp, "%s%s%02d:%02d:",
						fc_is_zero ? "" : " ",
						(fc_minus ? "-" : (fc_is_before ? "+" : "")),
						abs(fc_hour), abs(fc_min));
				fc_cp += strlen(fc_cp);
				fc_AppendSeconds(fc_cp, fc_sec, fc_fsec, MAX_INTERVAL_PRECISION, true);
			}
			break;

			/* 与postgresql < 8.4兼容，当DateStyle != 'iso'时 */
		case INTSTYLE_POSTGRES_VERBOSE:
		default:
			strcpy(fc_cp, "@");
			fc_cp++;
			fc_cp = fc_AddVerboseIntPart(fc_cp, fc_year, "year", &fc_is_zero, &fc_is_before);
			fc_cp = fc_AddVerboseIntPart(fc_cp, fc_mon, "mon", &fc_is_zero, &fc_is_before);
			fc_cp = fc_AddVerboseIntPart(fc_cp, fc_mday, "day", &fc_is_zero, &fc_is_before);
			fc_cp = fc_AddVerboseIntPart(fc_cp, fc_hour, "hour", &fc_is_zero, &fc_is_before);
			fc_cp = fc_AddVerboseIntPart(fc_cp, fc_min, "min", &fc_is_zero, &fc_is_before);
			if (fc_sec != 0 || fc_fsec != 0)
			{
				*fc_cp++ = ' ';
				if (fc_sec < 0 || (fc_sec == 0 && fc_fsec < 0))
				{
					if (fc_is_zero)
						fc_is_before = true;
					else if (!fc_is_before)
						*fc_cp++ = '-';
				}
				else if (fc_is_before)
					*fc_cp++ = '-';
				fc_AppendSeconds(fc_cp, fc_sec, fc_fsec, MAX_INTERVAL_PRECISION, false);
				fc_cp += strlen(fc_cp);
				/* 我们输出“ago”，而不是负数，所以使用abs()。 */
				sprintf(fc_cp, " sec%s",
						(abs(fc_sec) != 1 || fc_fsec != 0) ? "s" : "");
				fc_is_zero = false;
			}
			/* 完全为零？那么放入一个无单位的零... */
			if (fc_is_zero)
				strcat(fc_cp, " 0");
			if (fc_is_before)
				strcat(fc_cp, " ago");
			break;
	}
}


/* interval2tm()
 * 将间隔数据类型转换为tm结构。
 */
static int fc_interval2tm(interval fc_span, struct tm *fc_tm, fsec_t *fc_fsec)
{
	int64		fc_time;

	if (fc_span.month != 0)
	{
		fc_tm->tm_year = fc_span.month / MONTHS_PER_YEAR;
		fc_tm->tm_mon = fc_span.month % MONTHS_PER_YEAR;
	}
	else
	{
		fc_tm->tm_year = 0;
		fc_tm->tm_mon = 0;
	}

	fc_time = fc_span.time;

	fc_tm->tm_mday = fc_time / USECS_PER_DAY;
	fc_time -= fc_tm->tm_mday * USECS_PER_DAY;
	fc_tm->tm_hour = fc_time / USECS_PER_HOUR;
	fc_time -= fc_tm->tm_hour * USECS_PER_HOUR;
	fc_tm->tm_min = fc_time / USECS_PER_MINUTE;
	fc_time -= fc_tm->tm_min * USECS_PER_MINUTE;
	fc_tm->tm_sec = fc_time / USECS_PER_SEC;
	*fc_fsec = fc_time - (fc_tm->tm_sec * USECS_PER_SEC);

	return 0;
}								/* interval2tm() */

static int fc_tm2interval(struct tm *fc_tm, fsec_t fc_fsec, interval * fc_span)
{
	if ((double) fc_tm->tm_year * MONTHS_PER_YEAR + fc_tm->tm_mon > INT_MAX ||
		(double) fc_tm->tm_year * MONTHS_PER_YEAR + fc_tm->tm_mon < INT_MIN)
		return -1;
	fc_span->month = fc_tm->tm_year * MONTHS_PER_YEAR + fc_tm->tm_mon;
	fc_span->time = (((((((fc_tm->tm_mday * INT64CONST(24)) +
					   fc_tm->tm_hour) * INT64CONST(60)) +
					 fc_tm->tm_min) * INT64CONST(60)) +
				   fc_tm->tm_sec) * USECS_PER_SEC) + fc_fsec;

	return 0;
}								/* tm2interval() */

interval *
PGTYPESinterval_new(void)
{
	interval   *fc_result;

	fc_result = (interval *) pgtypes_alloc(sizeof(interval));
	/* 结果可以为 NULL 如果我们内存用尽 */
	return fc_result;
}

void PGTYPESinterval_free(interval * fc_intvl)
{
	free(fc_intvl);
}

interval *
PGTYPESinterval_from_asc(char *fc_str, char **fc_endptr)
{
	interval   *fc_result = NULL;
	fsec_t		fc_fsec;
	struct tm	fc_tt,
			   *fc_tm = &fc_tt;
	int			fc_dtype;
	int			fc_nf;
	char	   *fc_field[MAXDATEFIELDS];
	int			fc_ftype[MAXDATEFIELDS];
	char		fc_lowstr[MAXDATELEN + MAXDATEFIELDS];
	char	   *fc_realptr;
	char	  **fc_ptr = (fc_endptr != NULL) ? fc_endptr : &fc_realptr;

	fc_tm->tm_year = 0;
	fc_tm->tm_mon = 0;
	fc_tm->tm_mday = 0;
	fc_tm->tm_hour = 0;
	fc_tm->tm_min = 0;
	fc_tm->tm_sec = 0;
	fc_fsec = 0;

	if (strlen(fc_str) > MAXDATELEN)
	{
		errno = PGTYPES_INTVL_BAD_INTERVAL;
		return NULL;
	}

	if (ParseDateTime(fc_str, fc_lowstr, fc_field, fc_ftype, &fc_nf, fc_ptr) != 0 ||
		(DecodeInterval(fc_field, fc_ftype, fc_nf, &fc_dtype, fc_tm, &fc_fsec) != 0 &&
		 fc_DecodeISO8601Interval(fc_str, &fc_dtype, fc_tm, &fc_fsec) != 0))
	{
		errno = PGTYPES_INTVL_BAD_INTERVAL;
		return NULL;
	}

	fc_result = (interval *) pgtypes_alloc(sizeof(interval));
	if (!fc_result)
		return NULL;

	if (fc_dtype != DTK_DELTA)
	{
		errno = PGTYPES_INTVL_BAD_INTERVAL;
		free(fc_result);
		return NULL;
	}

	if (fc_tm2interval(fc_tm, fc_fsec, fc_result) != 0)
	{
		errno = PGTYPES_INTVL_BAD_INTERVAL;
		free(fc_result);
		return NULL;
	}

	errno = 0;
	return fc_result;
}

char * PGTYPESinterval_to_asc(interval * fc_span)
{
	struct tm	fc_tt,
			   *fc_tm = &fc_tt;
	fsec_t		fc_fsec;
	char		fc_buf[MAXDATELEN + 1];
	int			fc_IntervalStyle = INTSTYLE_POSTGRES_VERBOSE;

	if (fc_interval2tm(*fc_span, fc_tm, &fc_fsec) != 0)
	{
		errno = PGTYPES_INTVL_BAD_INTERVAL;
		return NULL;
	}

	EncodeInterval(fc_tm, fc_fsec, fc_IntervalStyle, fc_buf);

	return pgtypes_strdup(fc_buf);
}

int PGTYPESinterval_copy(interval * fc_intvlsrc, interval * fc_intvldest)
{
	fc_intvldest->time = fc_intvlsrc->time;
	fc_intvldest->month = fc_intvlsrc->month;

	return 0;
}
